package com.itita.ww2.utils.channel.google;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.Proxy.Type;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;

public class GoogleAuthCodeVerifier {
	private static final Logger logger = LoggerFactory.getLogger(GoogleAuthCodeVerifier.class);
	private Builder builder;

	private GoogleAuthCodeVerifier(Builder builder) {
		this.builder = builder;
	}

	public GoogleUserInfo verify(String authCode) {
		GoogleUserInfo ret = new GoogleUserInfo();
		String accessToken = exchangeAccessToken(authCode);
		if (accessToken == null) {
			return ret;
		}
		ret = getUserInfo(accessToken);
		return ret;
	}

	private String exchangeAccessToken(String authCode) {
		OkHttpClient httpClient = new OkHttpClient.Builder().proxy(builder.proxy).build();

		// @formatter:off
		RequestBody body = new FormBody.Builder()
				.add("client_id", builder.clientId)
				.add("client_secret", builder.clientSecret)
				.add("grant_type", "authorization_code")
				.add("code", authCode)
				.build();
		// @formatter:on

		Request request = new Request.Builder().url(builder.accessTokenUri).post(body).build();
		try {
			Response response = httpClient.newCall(request).execute();
			if (!response.isSuccessful()) {
				logger.error("exchange access_token error! code:{}", response.code());
				return null;
			}
			JsonObject data = new JsonParser().parse(response.body().string()).getAsJsonObject();
			if (data.has("error")) {
				logger.error("error: {}", data.toString());
				return null;
			}
			if (data.has("access_token")) {
				String ret = data.get("access_token").getAsString();
				return ret;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	private GoogleUserInfo getUserInfo(String accessToken) {
		GoogleUserInfo ret = new GoogleUserInfo();
		OkHttpClient httpClient = new OkHttpClient.Builder().proxy(builder.proxy).build();
		// @formatter:off
		Request request = new Request.Builder()
				.url(builder.playerIdUri)
				.addHeader("Authorization", "OAuth " + accessToken)
				.build();
		// @formatter:on
		try {
			Response response = httpClient.newCall(request).execute();
			if (!response.isSuccessful()) {
				return null;
			}
			JsonObject data = new JsonParser().parse(response.body().string()).getAsJsonObject();
			if (data.has("player_id")) {
				String playerId = data.get("player_id").getAsString();
				ret.setId(playerId);
				ret.setSucc(true);
				return ret;
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return ret;
	}

	@SuppressWarnings("unused")
	public static class Builder {
		private String accessTokenUri = "https://www.googleapis.com/oauth2/v4/token"; // post
		private String tokenInfoUri = "https://www.googleapis.com/oauth2/v1/tokeninfo"; // post
		private String playerIdUri = "https://www.googleapis.com/games/v1/applications/<app-id>/verify"; // get
		//
		private String clientId;
		private String clientSecret;
		private String appId;
		//
		private Proxy proxy = Proxy.NO_PROXY;

		// 
		public Builder(String clientId, String clientSecret, String appId) {
			this.clientId = clientId;
			this.clientSecret = clientSecret;
			this.appId = appId;
			// 
			this.playerIdUri = this.playerIdUri.replaceAll("<app-id>", this.appId);
		}

		public Builder setAccessTokenUri(String uri) {
			this.accessTokenUri = uri;
			return this;
		}

		public Builder setTokenInfoUri(String uri) {
			this.tokenInfoUri = uri;
			return this;
		}

		public Builder setPlayerIdUri(String uri) {
			this.playerIdUri = uri.replaceAll("<app-id>", this.appId);
			return this;
		}

		public Builder setPlayerIdUriFull(String uri) {
			this.playerIdUri = uri;
			return this;
		}

		public Builder setProxy(String type, String host, int port) {
			proxy = new Proxy(Type.valueOf(type.toUpperCase()), new InetSocketAddress(host, port));
			return this;
		}

		public GoogleAuthCodeVerifier build() {
			return new GoogleAuthCodeVerifier(this);
		}
	}

	public Builder getBuilder() {
		return builder;
	}
}
